#include <stdlib.h>
#include <stdio.h>
#include "mpi.h"
#include "dtypes.h"
#include "oom.h"

int MPI_Type_contiguous(int count, MPI_Datatype oldtype,
	MPI_Datatype *newtype)
{
    int rval;
    
    /* Contiguous datatypes can be created by
     * specialized calls to MPI_Type_vector.  While
     * this is slightly less efficient, it leaves
     * less chance for errors by reducing code. We
     * have decided to go this route, with the
     * chance to come back and rewrite for
     * efficiency later if needed */

    /* stride is arbitrary here */
    rval = MPI_Type_vector(1, count, 0, oldtype, newtype);
    
    /* The above call will run very closely to
     * a direct approach to MPI_Type_contiguous() */
    
    /* MPI_Type_vector(count, 1, 1, oldtype, newtype);
     * will also work, but it will cause slightly more
     * loop checking.  The stride is NOT arbitrary. */
    
    return rval;
}

int MPI_Type_vector(int count, int blocklength, int stride,
	MPI_Datatype oldtype, MPI_Datatype *newtype)
{
    int rval;
    int ext;
    MPI_Aint bytestride;

    /* Since the stride here is in elements 
     * ( stride * extent(oldtype) ), and the 
     * stride in hvector is in bytes (stride),
     * we can achieve the same results by calling 
     * hvector with a stride of ( stride * extent(oldtype) ), 
     * thus reducing chances for error. 
     * Note that this is basically the same as writing
     * vector out, as only one line of code would change. */

    MPI_Type_extent(oldtype, &ext);

    bytestride = (MPI_Aint) (stride * ext);
    
    rval = MPI_Type_hvector(count, blocklength, bytestride,
            oldtype, newtype);
        
    return rval;
}

int MPI_Type_hvector(int count, int blocklength, MPI_Aint stride,
	MPI_Datatype oldtype, MPI_Datatype *newtype)
{
	int rval, i;
    MPI_Datatype *array_of_types;
    int *array_of_displacements, *array_of_blocklengths;

    /* MPI_Type_hvector is likely the most difficult
     * constructor to make fit the mold of another.
     * However, it can be relatively easily made to
     * conform to the generic MPI_Type_struct model
     * by converting its arguments to arrays of 
     * arguments for struct.  The only calculation is
     * converting stride to displacement, which is
     * simple since both refer to the displacement between
     * the beginnings of blocks (NOT the end of one block
     * to the beginning of another).  Doing this causes
     * a slight amount of processing overhead, but gains
     * a great deal of ease of debugging and maintaining
     * the code base. */
    
    /* make a new array of datatypes */
    array_of_types = (MPI_Datatype *) OOM_palloc(count * sizeof(MPI_Datatype));

    /* make an array of blocklengths */
    array_of_blocklengths = (int *) OOM_palloc(count * sizeof(int));

    /* make an array of byte displacements */
    array_of_displacements = (int *) OOM_palloc(count * sizeof(int));

    for (i = 0; i < count; i++)
    {
        /* make all datatypes equal to oldtype */
        array_of_types[i] = oldtype;

        /* make all blocklengths equal to blocklength */
        array_of_blocklengths[i] = blocklength;

        /* displacement of corresponding block is simply
         * the stride times (block - 1) 
         * (e.g. first block at 0, next at "stride", etc.) */
        array_of_displacements[i] = stride * i;
    }

    rval = MPI_Type_struct(count, array_of_blocklengths,
            array_of_displacements, array_of_types,
            newtype);
    
    return rval;
}

int MPI_Type_indexed(int count, int *array_of_blocklengths,
	int *array_of_displacements, MPI_Datatype oldtype, MPI_Datatype *newtype)
{
    int rval, i, ext;
    int *array_of_byte_displacements;

    /* The only difference between MPI_Type_indexed and 
     * MPI_Type_hindexed is that while the array of 
     * displacements for indexed is given in multiples
     * of the extent of oldtype, hindexed takes its
     * array of displacements in bytes.  We can achieve
     * the same results by creating an array of byte-
     * valued displacements from the extent-based one
     * and calling MPI_Type_hindexed */

    array_of_byte_displacements = (int *) OOM_palloc(count * sizeof(int));

    MPI_Type_extent(oldtype, &ext);

    for (i = 0; i < count; i++)
        array_of_byte_displacements[i] = 
            array_of_displacements[i] * ext;

    rval = MPI_Type_hindexed(count, array_of_blocklengths,
            array_of_byte_displacements, oldtype, newtype);
    
    return rval;
}

int MPI_Type_hindexed(int count, int *array_of_blocklengths,
	MPI_Aint *array_of_displacements, MPI_Datatype oldtype,
	MPI_Datatype *newtype)
{
	int rval, i;
    MPI_Datatype *array_of_types;

    /* MPI_Type_hindexed operates in basically the same way
     * as MPI_Type_struct, with only a single datatype. We
     * can therefore achieve the same results by calling
     * MPI_Type_struct, with an array of all the same 
     * datatype */

    array_of_types = (MPI_Datatype *) OOM_palloc(count * sizeof(MPI_Datatype));

    for (i = 0; i < count; i++)
        array_of_types[i] = oldtype;

    rval = MPI_Type_struct(count, array_of_blocklengths,
            array_of_displacements, array_of_types,
            newtype);
    
    return rval;
}

int MPI_Type_struct(int count, int *array_of_blocklengths,
	MPI_Aint *array_of_displacements, MPI_Datatype *array_of_types,
	MPI_Datatype *newtype)
{
	int ext, modifier, typesize;
    int max_ub = -9999999, min_lb = 9999999;
    int i, j;
    Datatype *real_dtype = NULL, *real_newtype = NULL;
    Typemap *cur = NULL, *type_cur = NULL;

    /* create and get reference to new type */
    *newtype = OOM_datatype_create();
    real_newtype = OOM_datatype_lookup(*newtype);

    /* create a typemap with a dummy node, 
     * we'll check it later and remove it*/
    real_newtype->typemap = OOM_typemap_create();
    cur = real_newtype->typemap;
    /* just in case no real types are added, for debugging */
    cur->type = MPI_DATATYPE_NULL;

    for (i = 0; i < count; i++)
    {
        /* handle the cases for MPI_LB and MPI_UB */
        if (array_of_types[i] == MPI_LB)
        {
            /* no explicit LB yet */
            if (real_newtype->lb == NULL)
            {
                /* get a structure */
                real_newtype->lb = OOM_typemap_create();
                /* set its values */
                real_newtype->lb->type = MPI_LB;
                real_newtype->lb->disp = array_of_displacements[i];
            }
            /* displacement is less than current LB */
            else if (array_of_displacements[i] < real_newtype->lb->disp)
            {
                real_newtype->lb->disp = array_of_displacements[i];
            }
        }
        else if (array_of_types[i] == MPI_UB)
        {
            /* no explicit UB yet */
            if (real_newtype->ub == NULL)
            {
                /* get a structure */
                real_newtype->ub = OOM_typemap_create();
                /* set its values */
                real_newtype->ub->type = MPI_UB;
                real_newtype->ub->disp = array_of_displacements[i];
            }
            /* displacement is greater than current UB */
            else if (array_of_displacements[i] > real_newtype->ub->disp)
            {
                real_newtype->ub->disp = array_of_displacements[i];
            }
        }
        else /* regular datatype */
        {
            /* get a pointer to the actual datatype */
            real_dtype = OOM_datatype_lookup(array_of_types[i]);
            
            /* get the extent of the datatype */
            MPI_Type_extent(array_of_types[i], &ext);
            
            for (j = 0; j < array_of_blocklengths[i]; j++)
            {
                /* set the displacement modifier based on the 
                 * extent of the type being added. This will
                 * be needed for adding multiples (e.g. the 
                 * 3rd character in a block of characters at
                 * displacement 32 would have a displacement
                 * of 34 = 32 + 2 * ext(char) )*/
                modifier = ext * j;
                if (real_dtype == NULL) /* basic type, add it */
                {
                    /* make a new node */
                    cur->next = OOM_typemap_create();
                    cur = cur->next;
                    cur->next = NULL;
                    
                    /* set values */
                    cur->type = array_of_types[i];
                    /* must add a modifier after the first */
                    cur->disp = array_of_displacements[i] + 
                        modifier;

                    /* add the size to the total and check
                     * the natural bounds */
                    MPI_Type_size(cur->type, &typesize);
                    real_newtype->size += typesize;

                    if (min_lb > cur->disp)
                        min_lb = cur->disp;
                    if (max_ub < (cur->disp + typesize))
                        max_ub = cur->disp + typesize;
                }
                else /* derived type */
                {
                    /*************************************
                     * Handle LB and UB within this type *
                     ************************************/
                    if (real_dtype->lb != NULL)
                    {
                        /* no explicit LB yet */
                        if (real_newtype->lb == NULL)
                        {
                            /* get a structure */
                            real_newtype->lb = OOM_typemap_create();
                            /* set its values */
                            real_newtype->lb->type = MPI_LB;
                            real_newtype->lb->disp = 
                                real_dtype->lb->disp + 
                                array_of_displacements[i] + modifier;
                        }
                        /* displacement is less than current LB */
                        else if (real_dtype->lb->disp <
                                (real_newtype->lb->disp +
                                array_of_displacements[i] + modifier))
                        {
                            real_newtype->lb->disp =
                                real_dtype->lb->disp + 
                                array_of_displacements[i] + modifier;
                        }
                    }
                    if (real_dtype->ub != NULL)
                    {
                        /* no explicit UB yet */
                        if (real_newtype->ub == NULL)
                        {
                            /* get a structure */
                            real_newtype->ub = OOM_typemap_create();
                            /* set its values */
                            real_newtype->ub->type = MPI_UB;
                            real_newtype->ub->disp =
                                real_dtype->ub->disp + 
                                array_of_displacements[i] + modifier;
                        }
                        /* displacement is greater than current UB */
                        else if (real_dtype->ub->disp >
                                (real_newtype->ub->disp +
                                array_of_displacements[i] + modifier))
                        {
                            real_newtype->ub->disp =
                                real_dtype->ub->disp + 
                                array_of_displacements[i] + modifier;
                        }
                    }
                    
                    /*******************************
                     * Handle the rest of the type *
                     ******************************/
                    
                    /* get a pointer to the typemap */
                    type_cur = real_dtype->typemap;
                    /* add each subtype and displacement */
                    while (type_cur != NULL)
                    {
                        /* make a new node */
                        cur->next = OOM_typemap_create();
                        cur = cur->next;
                        cur->next = NULL;

                        /* set values */
                        cur->type = type_cur->type;
                        /* add a modifier after the first */
                        cur->disp = array_of_displacements[i] + 
                            type_cur->disp + modifier;

                        /* next subtype */
                        type_cur = type_cur->next;

                        /* add the size to the total and check
                         * the bounds */
                        MPI_Type_size(cur->type, &typesize);
                        real_newtype->size += typesize;

                        if (min_lb > cur->disp)
                            min_lb = cur->disp;
                        if (max_ub < (cur->disp + typesize))
                            max_ub = cur->disp + typesize;
                    }
                }
            }

            /***************************************************
             * Add the total # of new elements to num_elements *
             * NOTE: Done down here just for code cleanliness  *
             **************************************************/
            /* base type, 1 element per entry*/
            if (real_dtype == NULL)
                real_newtype->num_elements += array_of_blocklengths[i];
            /* user-defined type, get num_elements attribute */
            else
                real_newtype->num_elements += (real_dtype->num_elements * array_of_blocklengths[i]);
        }
    }

    /***********************************
     * Find the extent of the new type *
     **********************************/
    /* find the lower bound */
    if (real_newtype->lb != NULL)
        /* explicit, use it */
        min_lb = real_newtype->lb->disp;
    /* else not given, use the min_lb */

    /* find the upper bound */
    if (real_newtype->ub != NULL)
        /* explicit, use it */
        max_ub = real_newtype->ub->disp;
    /* else not given, use the max_ub */

    /* calculate the extent */
    real_newtype->extent = (MPI_Aint) (max_ub - min_lb);

    /* check for "empty" typemap and remove the dummy.
     * The memory pool will clean up after us */
    if (real_newtype->typemap->next == NULL)
        real_newtype->typemap = NULL; /* just make it empty */
    else
        real_newtype->typemap = real_newtype->typemap->next;

        return MPI_SUCCESS;
}

int MPI_Address(void *location, MPI_Aint *address)
{
    /* find the number of bytes between MPI_BOTTOM and location 
     * by subtracting the byte address of MPI_BOTTOM from that 
     * of location (in our implementation, chars are bytes, so
     * char ptr will give us the byte address) */

    *address = (MPI_Aint) ((char *)location - (char *)MPI_BOTTOM);

    return MPI_SUCCESS;
}

int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint *extent)
{
	MPI_Aint tmpext;
    Datatype *real_dtype;
    
    tmpext = (MPI_Aint) sizeof_base_type(datatype);
    
    if (tmpext == -1) /* if not a base type */
    {
        /* get the actual data type and check its extent */
        real_dtype = OOM_datatype_lookup(datatype);
        tmpext = real_dtype->extent;
    }/* else the extent will also be the size */

    /* set the result and return */
    *extent = tmpext;

    return MPI_SUCCESS;
}

int MPI_Type_size(MPI_Datatype datatype, int *size)
{
	int tmpsize;
    Datatype *real_dtype;
    
    tmpsize = sizeof_base_type(datatype);
    
    if (tmpsize == -1) /* if not a base type */
    {
        /* get the actual data type and check its size entry */
        real_dtype = OOM_datatype_lookup(datatype);
        tmpsize = real_dtype->size;
    }

    /* set the result and return */
    *size = tmpsize;

    return MPI_SUCCESS;
}

int MPI_Type_lb(MPI_Datatype datatype, MPI_Aint *displacement)
{
    Datatype *real_dtype = NULL;

    real_dtype = OOM_datatype_lookup(datatype);

    if (real_dtype == NULL) /* base type, displacement 0 */
        *displacement = (MPI_Aint) 0;
    else
    {
        if (real_dtype->lb != NULL) /* explicit lb */
        {
            *displacement = real_dtype->lb->disp;
        }
        else /* natural lb */
        {
            /* cover empty types */
            if (real_dtype->typemap != NULL)
                /* first displacement */
                *displacement = real_dtype->typemap->disp;
            else
                /* 0 by default */
                *displacement = 0;
        }
    }

    return MPI_SUCCESS;
}

int MPI_Type_ub(MPI_Datatype datatype, MPI_Aint *displacement)
{
    MPI_Aint lb, extent;
    int rval;

    /* NOTE: Since natural upper bounds require calculations
     * which are already done to calculate the extent, let's
     * just calculate the upper bound for simplicity */
    
    /* get lower bound and extent */
    if ((rval = MPI_Type_lb(datatype, &lb)) != MPI_SUCCESS)
        return rval;
    if ((rval = MPI_Type_extent(datatype, &extent)) != MPI_SUCCESS)
        return rval;

    /* upper bound is lower bound plus extent */
    *displacement = lb + extent;

    return MPI_SUCCESS;
}

int MPI_Type_commit(MPI_Datatype *datatype)
{
	Datatype *real_dtype = NULL;

    real_dtype = OOM_datatype_lookup(*datatype);

    if (real_dtype != NULL)
        real_dtype->committed = 1;

    return MPI_SUCCESS;
}

int MPI_Type_free(MPI_Datatype *datatype)
{
    /* For now, this just "un-commits" the datatype.
     * Eventually, this should set up the datatype for
     * removal from the system, but all communications
     * using this type must be completed first */
	Datatype *real_dtype = NULL;

    real_dtype = OOM_datatype_lookup(*datatype);

    if (real_dtype != NULL)
        real_dtype->committed = 0;

    *datatype = MPI_DATATYPE_NULL;

    return MPI_SUCCESS;
}

int MPI_Get_elements(MPI_Status *status, MPI_Datatype datatype, int *count)
{
    Datatype *real_dtype = NULL;
    Typemap *cur = NULL;
    int size, tmpcount, tmpbytes;
    int ierr;

    /* derived type */
    if (datatype >= MPI_DATATYPE_BASIC_MAX)
    {
        real_dtype = OOM_datatype_lookup(datatype);
        if (real_dtype == NULL)
            return MPI_ERR_TYPE;
    }

    ierr = MPI_Type_size(datatype, &size);
    if (ierr != MPI_SUCCESS) return MPI_ERR_TYPE;

    /* get number of bytes received */
    tmpbytes = status->bytes_recvd;

    /* get number of whole elements received */
    tmpcount = tmpbytes / size;
    /* if derived, multiply by num_elements */
    if (real_dtype != NULL)
        tmpcount = tmpcount * real_dtype->num_elements;

    /* get remainder of bytes */
    tmpbytes = tmpbytes % size;

    /* if derived, get number of subelements */
    if (real_dtype != NULL)
    {
        /* get first basic element and size */
        cur = real_dtype->typemap;
        if (cur != NULL) 
        {
            ierr = MPI_Type_size(cur->type, &size);
            if (ierr != MPI_SUCCESS) return MPI_ERR_TYPE;
        }

        /* continue adding to count and deducting
         * byte total until no more sizes can be
         * deducted */
        while (tmpbytes >= size && cur != NULL)
        {
            tmpcount++;
            tmpbytes = tmpbytes - size;

            /* get next basic element and size */
            cur = cur->next;
            if (cur != NULL) 
            {
                ierr = MPI_Type_size(cur->type, &size);
                if (ierr != MPI_SUCCESS) return MPI_ERR_TYPE;
            }
        }
    }

    /* set parameter and return success */
    *count = tmpcount;
    
    return MPI_SUCCESS;	
}

int MPI_Pack(void *inbuf, int incount, MPI_Datatype datatype, void *outbuf,
	int outsize, int *position,  MPI_Comm comm)
{
    int i, ierr;
    int size, offset;
    MPI_Aint ext;
    Datatype *real_dtype = NULL;
    Typemap *cur = NULL;

    /* basic type */
    if (datatype < MPI_DATATYPE_BASIC_MAX)
    {
        /* get type size */
        ierr = MPI_Type_size(datatype, &size);
        if (ierr != MPI_SUCCESS) return MPI_ERR_OTHER;
        if (size < 0) return MPI_ERR_TYPE;
        /* nothing to copy */
        if (size == 0) return MPI_SUCCESS;

        /* initialization */
        offset = 0;
        /* pack <incount> items */
        for (i = 0; i < incount; i++)
        {
            /* using memmove in case buffers overlap */
            memmove(outbuf + *position, inbuf + offset, size);
            *position += size;
            offset += size;
        }
    }   
    /* derived type */
    else
    {
        /* get actual type definition */
        real_dtype = OOM_datatype_lookup(datatype);
        if (real_dtype == NULL)
            return MPI_ERR_TYPE;

        /* get extent of type for offset */
        ierr = MPI_Type_extent(datatype, &ext);
        if (ierr != MPI_SUCCESS)
            return MPI_ERR_TYPE;

        /* copy <incount> items */
        for (i = 0; i < incount; i++)
        {
            cur = real_dtype->typemap;
            offset = i * (int) ext;
            /* nothing to copy */
            if (cur == NULL)
                return MPI_SUCCESS;
            
            /* copy one of each subtype */
            while (cur != NULL)
            {
                /* get the subtype size */
                ierr = MPI_Type_size(cur->type, &size);
                if (ierr != MPI_SUCCESS)
                    return MPI_ERR_TYPE;

                /* using memmove in case buffers overlap */
                memmove(outbuf + *position, 
                        inbuf + cur->disp + offset, size);

                cur = cur->next;
                *position += size;
            }
        }
    }

    /* Check for buffer overflow; return error if found. 
     * This is the only error checking done on buffer size,
     * therefore, memory may be corrupted by user's call. */
    if (*position > outsize)
        return MPI_ERR_OTHER;

	return MPI_SUCCESS;
}

int MPI_Unpack(void *inbuf, int insize, int *position, void *outbuf,
	int outcount, MPI_Datatype datatype, MPI_Comm comm)
{
    int i, ierr;
    int size, offset;
    MPI_Aint ext;
    Datatype *real_dtype = NULL;
    Typemap *cur = NULL;

    /* NOTE: This is basically the exact same (in logic) 
     * as MPI_Pack, except the direction of the copy and 
     * some variable names are different. */
    
    /* basic type */
    if (datatype < MPI_DATATYPE_BASIC_MAX)
    {
        /* get type size */
        ierr = MPI_Type_size(datatype, &size);
        if (ierr != MPI_SUCCESS) return MPI_ERR_OTHER;
        if (size < 0) return MPI_ERR_TYPE;
        /* nothing to copy */
        if (size == 0) return MPI_SUCCESS;

        /* initialization */
        offset = 0;
        /* pack <incount> items */
        for (i = 0; i < outcount; i++)
        {
            /* using memmove in case buffers overlap */
            memmove(outbuf + offset, inbuf + *position, size);
            *position += size;
            offset += size;
        }
    }   
    /* derived type */
    else
    {
        /* get actual type definition */
        real_dtype = OOM_datatype_lookup(datatype);
        if (real_dtype == NULL)
            return MPI_ERR_TYPE;

        /* get extent of type for offset */
        ierr = MPI_Type_extent(datatype, &ext);
        if (ierr != MPI_SUCCESS)
            return MPI_ERR_TYPE;

        /* copy <incount> items */
        for (i = 0; i < outcount; i++)
        {
            cur = real_dtype->typemap;
            offset = i * (int) ext;
            /* nothing to copy */
            if (cur == NULL)
                return MPI_SUCCESS;
            
            /* copy one of each subtype */
            while (cur != NULL)
            {
                /* get the subtype size */
                ierr = MPI_Type_size(cur->type, &size);
                if (ierr != MPI_SUCCESS)
                    return MPI_ERR_TYPE;

                /* using memmove in case buffers overlap */
                memmove(outbuf + cur->disp + offset, 
                        inbuf + *position, size);

                cur = cur->next;
                *position += size;
            }
        }
    }

    /* Check for buffer overflow; return error if found. 
     * This is the only error checking done on buffer size,
     * therefore, memory may be corrupted by user's call.
     * Here, it is most likely that bad data was copied. */
    if (*position > insize)
        return MPI_ERR_OTHER;

	return MPI_SUCCESS;
}

int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm,
	int *size)
{
    int typesize;
    int ierr;
    
    /* packing data requires size(datatype) * incount bytes */
    ierr = MPI_Type_size(datatype, &typesize);
    if (ierr != MPI_SUCCESS) return MPI_ERR_TYPE;

    *size = typesize * incount;
    
	return MPI_SUCCESS;
}

int sizeof_base_type(MPI_Datatype datatype)
{
	switch (datatype)
	{
		case MPI_CHAR:
			return sizeof(char);
		case MPI_SHORT:
			return sizeof(short);
		case MPI_INT:
			return sizeof(int);
		case MPI_LONG:
			return sizeof(long);
		case MPI_UNSIGNED_CHAR:
			return sizeof(unsigned char);
		case MPI_UNSIGNED_SHORT:
			return sizeof(unsigned short);
		case MPI_UNSIGNED:
			return sizeof(unsigned int);
		case MPI_UNSIGNED_LONG:
			return sizeof(unsigned long);
		case MPI_FLOAT:
			return sizeof(float);
		case MPI_DOUBLE:
			return sizeof(double);
		case MPI_LONG_DOUBLE:
			return sizeof(long double);
		case MPI_BYTE:
		case MPI_PACKED:
			return sizeof(char);
		case MPI_LONG_LONG_INT:
			return sizeof(long long);
		case MPI_LB:
		case MPI_UB:
        case MPI_DATATYPE_NULL:
			return 0;
		default: /* return -1 if not a base type */
			return -1;
	}
}

/* Function: TEST_print_type
 * Author: Cliff Taylor
 * Description: This is an internal test function
 *              that will print a datatype's type
 *              and displacement values to stderr.
 */
void TEST_print_type(MPI_Datatype datatype)
{
    Datatype *real_dtype = NULL;
    Typemap *cur = NULL;

    real_dtype = OOM_datatype_lookup(datatype);

    if (real_dtype == NULL)
    {
        fprintf(stderr, "Basic or invalid type: %d\n", datatype);
        return;
    }
    else
        fprintf(stderr, "Datatype: %d\n", datatype);

    cur = real_dtype->typemap;

    /* print any explicit lower bound */
    if (real_dtype->lb != NULL)
    {
        fprintf(stderr, "MPI_LB -> %d\n",
                real_dtype->lb->disp);
    }
    
    /* if type is empty, print message */
    if (cur == NULL)
        fputs("Empty type, no default typemap\n", stderr);
    /* if type is not empty, print typemap/displacements */
    else
    {
        while (cur != NULL)
        {
            fprintf(stderr, "type: %d, disp: %d\n",
                    cur->type, cur->disp);

            /* next entry */
            cur = cur->next;
        }
    }

    /* print any explicit upper bound */
    if (real_dtype->ub != NULL)
    {
        fprintf(stderr, "MPI_UB -> %d\n",
                real_dtype->ub->disp);
    }
    
    /* also print size, extent, and committed entries */
    fprintf(stderr, "num_elements = %d\n", 
            real_dtype->num_elements);
    fprintf(stderr, "size = %d\n", real_dtype->size);
    fprintf(stderr, "extent = %d\n", real_dtype->extent);
    fprintf(stderr, "committed = %d\n", real_dtype->committed);
}
